# Python 类实例对象访问权限控制

# 访问权限控制是面向对象编程中一个重要的概念。
# 访问权限控制的目的主要是为了保护类的内部数据不被外部代码随意修改，同时也为了提高代码的可读性和可维护性。

# 在Python中，访问权限控制主要有以下几种：
# 公开属性：公开属性是指在类的外部可以直接访问的属性，可以直接通过类实例对象访问。
# 私有属性：私有属性是指在类的外部不能直接访问的属性，只能通过类的方法进行访问。
# 受保护属性：受保护属性是指在类的内部可以直接访问的属性，但不能直接通过类实例对象访问。


# 公开属性 - 相当于 Java 中的 public 属性
# 公开属性是指在类的外部可以直接通过类实例对象访问。
# 公开属性的定义和使用方式与普通的变量相同，只不过需要在变量名前加上self。

# 私有属性 - 相当于 Java 中的 private 属性
# 私有属性是指在类的外部不能被直接访问，只能在类的内部访问，不能通过类实例对象访问。
# 私有属性的定义需要在变量名前加上双下滑线(__)前缀。

# 受保护属性 - 相当于 Java 中的 protected 属性
# 受保护属性一般用于继承的场景，在子类中可以直接访问父类的受保护属性。
# 受保护属性的定义需要在变量名前加上单下划线(_)前缀。





# 定义Person类
class Person:
    def __init__(self, name, age, gender):
        # 定义一个公开变量
        self.name = name
        # 定义一个私有变量 - 双下滑线(__)前缀同于定义私有变量
        self.__age = age
        # 定义一个受保护变量 - 单下划线(_前缀)同于定义受保护变量
        self._gender = gender

    def get_name(self):
        return self.name

    def set_name(self, name):
        self.name = name

    def get_age(self):
        return self.__age

    # 定义一个私有方法 - 双下滑线(__)前缀同于定义私有方法
    # 方法无法通过实例对象直接调用
    def __get_age(self):
        return self.__age

    def set_age(self, age):
        self.__age = age

    def get_gender(self):
        return self._gender

    # 定义一个受保护方法 - 单下划线(_前缀)同于定义受保护方法
    def _get_gender(self):
        return self._gender

    def set_gender(self, gender):
        self._gender = gender

    def introduce(self):
        # 在类的内部可以直接访问私有属性和受保护属性
        print("我是", self.name, "，今年", self.__age, "岁，性别是", self._gender)
        # 在类的内部可以直接调用私有方法和受保护方法
        print(f"我是{self.get_name()}，今年{self.__get_age()}岁，性别是{self._get_gender()}")


p = Person("铁爪鬼王", 800, "人妖")

# 访问公开属性
print("公共属性：", p.name)  # 铁爪鬼王

# 访问私有属性 - 私有属性无法通过实例对象直接访问
# print(p.__age)
# 通过方法访问私有属性
print("通过公开方法访问私有属性：", p.get_age())

# 通过实例调用私有方法 - 可以看到私有方法无法通过实例对象直接调用
# print(p.__get_age())

# 访问受保护属性 - 受保护的属性可以被类实例对象访问
# 通过实例对象访问受保护属性时会出现警告，但仍然可以访问，不建议这么干
print("受保护的属性：", p._gender)

# 调用受保护方法 - 受保护方法可以被类实例对象调用
# 通过实例对象访问受保护方法时会出现警告，但仍然可以访问，不建议这么干
print("受保护的方法：", p._get_gender())

# 介绍完整信息
p.introduce()

print("-----------------------------------")

# 创建一个新的示例对象并初始化数据
person = Person("小明", 18, "男")

# 重新设置私有属性的值(看上去像是修改了私有属性，实际上是给实例对象添加了一个新属性(__age))
# 这总骚操作很容易造成代码的混乱，最好别这么干
person.__age = 22

# 通过公开方法访问私有属性
# 可以看到打印出来还是之前的初始化值18
print(person.get_age())

# 通过 __dict__ 属性可以看到实例对象中添加了新的属性(__age)
# 'name': '小明' 是公开属性
# '_Person__age': 18 是私有属性
# '_gender': '男' 是受保护属性
# '__age': 22 是新增的公开属性
print(person.__dict__)  # {'name': '小明', '_Person__age': 18, '_gender': '男', '__age': 22}

# 这里实际上是访问的新增的公开属性(__age)
print("新增的公开属性：", person.__age)

print("-----------------------------------")
# 打破访问规则限制
# 通过 __dict__ 可以看到 Python 内部将私有化变量进行了重命名：_类名__变量名
# 只是在语法上做了私有化控制，实际上还是可以访问的，所以可以通过 _类名__变量名 访问私有变量
person._Person__age = 31
# 可以看到私有化变量的值已经被修改
print(person.get_age())  # 31
print(person.__dict__)  # {'name': '小明', '_Person__age': 31, '_gender': '男', '__age': 22}

# 以上骚操作虽然可以这么干，但还是最好别这么干，不然会被揍
# 正常操作是通过公开方法来操作访问私有属性和方法